package com.edu.chapter07;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.edu.chapter07.listener.ChildGestureListener;

public class MyViewGroup extends ViewGroup {
    private int horizontalSpacing = 100; // 子View水平间距（单位px）
    private int verticalSpacing = 50;   // 行间距（单位px）

//    private final SparseArray<Rect> childPositions = new SparseArray<>();

    // 构造方法（支持XML布局）
    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        parseCustomAttrs(attrs); // 解析XML自定义属性
    }

    // 解析xml自定义属性
    private void parseCustomAttrs(AttributeSet attrs) {
        // obtainStyledAttributes(): 通过当前 Context 关联的主题和布局属性（attrs）生成 TypedArray 对象
        // 参数 R.styleable.MyViewGroup：编译时生成的整型数组，对应 attrs.xml 中定义的属性集合
        // （如 horizontalSpacing 的索引）
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.MyViewGroup);
        // 将属性值转换为像素单位（px），适配屏幕密度。
        // 参数1：属性索引（如 R.styleable.MyViewGroup_horizontalSpacing）。
        // 参数2：默认值（若 XML 未设置则使用该值）
        // getDimensionPixelSize() 自动将 dp/sp 转换为 px，避免开发者手动处理屏幕密度
        horizontalSpacing = ta.getDimensionPixelSize(R.styleable.MyViewGroup_horizontalSpacing, horizontalSpacing);
        verticalSpacing = ta.getDimensionPixelSize(R.styleable.MyViewGroup_verticalSpacing, verticalSpacing);
        // TypedArray 内部共享缓存资源，必须回收以防止内存泄漏
        ta.recycle();
    }

    // 支持margin
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    // 测量逻辑（核心）

    /**
     * onMeasure 是自定义 ViewGroup 测量子视图尺寸并确定自身尺寸的核心方法。在流式布局中，需实现以下目标：
     * 动态换行：子视图水平空间不足时自动换行。
     * 高度计算：累加所有行的高度（含间距和边距）。
     * 尺寸约束：适配父容器的 MeasureSpec（如 wrap_content 或固定高度）
     *
     * @param widthMeasureSpec  horizontal space requirements as imposed by the parent.
     *                          The requirements are encoded with
     *                          {@link android.view.View.MeasureSpec}.
     * @param heightMeasureSpec vertical space requirements as imposed by the parent.
     *                          The requirements are encoded with
     *                          {@link android.view.View.MeasureSpec}.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 父容器允许的最大宽度
        // 从 widthMeasureSpec 中提取父容器允许的最大宽度（含模式与尺寸）
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//        累加容器内已占用空间，初始值为 padding，避免内容溢出边界
        // 已用宽度（左右边距）
        int widthUsed = getPaddingLeft() + getPaddingRight(); // 已用宽度
        // 已用高度（起始为上边距）
        int heightUsed = getPaddingTop(); // 已用高度
        int lineHeight = 0; // 当前行高度
        int childCount = getChildCount();

        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) continue;

            // measureChildWithMargins()
            // 系统方法，自动处理子视图的 margin 和父容器 padding，生成正确的子视图 MeasureSpec
            // 测量子View（含margin）
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 子视图总占 宽/高：
            //childWidth 和 childHeight 包含视图内容尺寸 + 边距（margin），确保布局时空间计算准确
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            // 换行判断：当前行宽度不足时换行
            if (widthUsed + childWidth + horizontalSpacing > widthSize) {
                // 换行操作：
                //垂直累加：heightUsed 增加上一行高度（lineHeight）和行间距（verticalSpacing）。
                heightUsed += lineHeight + verticalSpacing; // 换行：累加行高和间距
                //水平重置：widthUsed 回到行起始位置（getPaddingLeft()）
                widthUsed = getPaddingLeft(); // 重置行起始位置
                lineHeight = 0;
            }
            // 累加当前行宽度（含子视图宽度和间距）
            widthUsed += childWidth + horizontalSpacing;
            // 更新当前行最大高度
            // 行高更新：取当前行所有子视图的最大高度（含 margin），确保行高足够容纳最高子视图
            lineHeight = Math.max(lineHeight, childHeight); // 更新行高
        }
        // 确定容器最终尺寸
        int totalHeight = heightUsed + lineHeight + getPaddingBottom();
        // resolveSize()：
        //关键方法！
        // 根据父容器的 heightMeasureSpec 模式调整最终高度：
        // EXACTLY：直接使用父容器指定高度（如 match_parent）。
        // AT_MOST（wrap_content）：取 totalHeight 和父容器允许高度的较小值。
        // UNSPECIFIED：直接使用 totalHeight
        setMeasuredDimension(widthSize, resolveSize(totalHeight, heightMeasureSpec));
    }

    // 布局逻辑（核心）

    /**
     * onLayout 是自定义 ViewGroup 布局子视图的核心方法，负责动态定位子视图位置并实现自动换行。其核心目标包括：
     * 动态换行：当水平空间不足时，自动将子视图放置到下一行。
     * 精准定位：计算每个子视图的左上角坐标 (left, top)。
     * 空间管理：通过坐标累加机制高效利用布局空间
     * 。
     *
     * @param changed This is a new size or position for this view
     * @param l       Left position, relative to parent
     * @param t       Top position, relative to parent
     * @param r       Right position, relative to parent
     * @param b       Bottom position, relative to parent
     */
    @SuppressLint({"ClickableViewAccessibility", "UseCompatLoadingForDrawables"})
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
//        childPositions.clear();
        // currentX/currentY：记录当前子视图的布局起点，初始值包含父容器的 padding，避免内容溢出边界
        int currentX = getPaddingLeft();  // 行起始X坐标（含左padding）
        int currentY = getPaddingTop();   // 当前行Y坐标（含上padding）
        int lineHeight = 0;              // 当前行最大高度

        // 遍历子视图并定位
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) continue;

            // 通过 MarginLayoutParams 获取子视图的 margin 值，确保布局空间计算准确
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 复用测量结果：直接使用 child.getMeasuredWidth/Height()，避免重复测量（依赖 onMeasure 阶段的结果）
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            // 换行判断
            if (currentX + childWidth + lp.rightMargin > getWidth() - getPaddingRight()) {
                currentY += lineHeight + verticalSpacing; // 换行：下移行高+间距
                currentX = getPaddingLeft();              // 重置行起始X坐标
                lineHeight = 0;                           // 重置行高
            }

            // 计算子View坐标
            int left = currentX + lp.leftMargin;  // 子视图左上角X坐标
            int top = currentY + lp.topMargin;    // 子视图左上角Y坐标
            // 通过 child.layout() 设置子视图的边界位置（相对于父容器坐标系）
            child.layout(left, top, left + childWidth, top + childHeight); // 定位子视图
//            // 存储位置信息
//            @SuppressLint("DrawAllocation") Rect rect = new Rect(left, top, left + childWidth, top + childHeight);
//            childPositions.put(i, rect);
            // 更新坐标
            currentX = left + childWidth + lp.rightMargin + horizontalSpacing; // 更新当前行X坐标
            lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); // 更新行高

            // 可添加子控件属性控制逻辑
            if (child instanceof TextView) {
                ((TextView) child).setTextColor(Color.GRAY);
                child.setBackground(getContext().getDrawable(R.drawable.textview_background));
                child.setPadding(50, 50, 50, 50);
            }

            @SuppressLint("DrawAllocation") ChildGestureListener listener = new ChildGestureListener(child);
            @SuppressLint("DrawAllocation") GestureDetector detector = new GestureDetector(getContext(), listener);

            child.setOnTouchListener((v, event) -> {
                detector.onTouchEvent(event); // 传递手势事件
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    listener.insertViewAtPosition(); // 松手时触发插入
                }
                return true;
            });

        }
    }
}